{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Jxv6goXm7oGF"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors.\n",
        "\n",
        "Licensed under the Apache License, Version 2.0 (the \"License\");"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "llMNufAK7nfK"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\"); { display-mode: \"form\" }\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8Byow2J6LaPl"
      },
      "source": [
        "# Лучшая производительность с tf.function с AutoGraph"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kGXS3UWBBNoc"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/guide/function\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Смотрите на TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ru/guide/function.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Запустите в Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ru/guide/function.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Изучайте код на GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ru/guide/function.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Скачайте ноутбук</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CydFK2CL7ZHA"
      },
      "source": [
        "TF 2.0 объединяет в себе простоту eager execution и мощь TF 1.0. В центре этого слияния находится `tf.function`, позволяющий преобразовывать подмножество синтаксиса Python в переносимые высокопроизводительные графы TensorFlow.\n",
        "\n",
        "AutoGraph - крутая новая функция `tf.function`, которая позволяет писать код графа с использованием естественного синтаксиса Python. Список возможностей Python, которые можно использовать с AutoGraph, см. в\n",
        "[Возможности и ограничения AutoGraph](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/g3doc/reference/limitations) мкр. Для  дополнительной информации о `tf.function` см. RFC\n",
        "[TF 2.0: функции, а не сессии](https://github.com/tensorflow/community/blob/master/rfcs/20180918-functions-not-sessions-20.md). Для более подробной информации об AutoGraph, см. `tf.autograph`.\n",
        "\n",
        "Этот учебник познакомит вас с базовыми функциями `tf.function` и AutoGraph."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "n4EKOpw9mObL"
      },
      "source": [
        "## Setup\n",
        "\n",
        "Импортируйте TensorFlow 2.0:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "V9oECvVSI1Kj"
      },
      "outputs": [],
      "source": [
        "import numpy as np"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "mT7meGqrZTz9"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "77AsVr1GGtBP"
      },
      "source": [
        "## Декоратор `tf.function`\n",
        "\n",
        "Когда вы аннотируете функцию с помощью` tf.function`, вы все равно можете вызывать ее как любую другую функцию. Но она будет скомпилирована в граф, что означает, что вы получаете преимущества более быстрого выполнения, запуска на GPU или TPU или экспорта в SavedModel. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "FhIg7-z6HNWj"
      },
      "outputs": [],
      "source": [
        "@tf.function\n",
        "def simple_nn_layer(x, y):\n",
        "  return tf.nn.relu(tf.matmul(x, y))\n",
        "\n",
        "\n",
        "x = tf.random.uniform((3, 3))\n",
        "y = tf.random.uniform((3, 3))\n",
        "\n",
        "simple_nn_layer(x, y)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "U-LAE4pMNR9g"
      },
      "source": [
        "Если мы посмотрим на результат аннотации, мы увидим, что это специально вызываемая функция, которая обрабатывает все взаимодействия со средой выполнения TensorFlow."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "q4t2iuS7Nqc0"
      },
      "outputs": [],
      "source": [
        "simple_nn_layer"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DqeefLGNXjZQ"
      },
      "source": [
        "Если ваш код использует несколько функций, вам не нужно аннотировать их все - любые функции, вызываемые из аннотированной функции, также будут работать в режиме графа."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3VGF7tlVXiZY"
      },
      "outputs": [],
      "source": [
        "def linear_layer(x):\n",
        "  return 2 * x + 1\n",
        "\n",
        "\n",
        "@tf.function\n",
        "def deep_net(x):\n",
        "  return tf.nn.relu(linear_layer(x))\n",
        "\n",
        "\n",
        "deep_net(tf.constant((1, 2, 3)))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yQvg6ZSKWyqE"
      },
      "source": [
        "Функции могут быть быстрее, чем eager код, для графов с большим количеством маленьких операций. Но для графов с несколькими дорогими операциями (например, свертки) вы можете не увидеть большого ускорения.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "0EL6lVwEWuFo"
      },
      "outputs": [],
      "source": [
        "import timeit\n",
        "conv_layer = tf.keras.layers.Conv2D(100, 3)\n",
        "\n",
        "@tf.function\n",
        "def conv_fn(image):\n",
        "  return conv_layer(image)\n",
        "\n",
        "image = tf.zeros([1, 200, 200, 100])\n",
        "# warm up\n",
        "conv_layer(image); conv_fn(image)\n",
        "print(\"Eager conv:\", timeit.timeit(lambda: conv_layer(image), number=10))\n",
        "print(\"Function conv:\", timeit.timeit(lambda: conv_fn(image), number=10))\n",
        "print(\"Обратите внимание, что для операций свертки нет большой разницы в производительности\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "L4zj-jpH0jKH"
      },
      "outputs": [],
      "source": [
        "lstm_cell = tf.keras.layers.LSTMCell(10)\n",
        "\n",
        "@tf.function\n",
        "def lstm_fn(input, state):\n",
        "  return lstm_cell(input, state)\n",
        "\n",
        "input = tf.zeros([10, 10])\n",
        "state = [tf.zeros([10, 10])] * 2\n",
        "# warm up\n",
        "lstm_cell(input, state); lstm_fn(input, state)\n",
        "print(\"eager lstm:\", timeit.timeit(lambda: lstm_cell(input, state), number=10))\n",
        "print(\"function lstm:\", timeit.timeit(lambda: lstm_fn(input, state), number=10))\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ohbSnA79mcJV"
      },
      "source": [
        "## Используйте порядок выполнения Python\n",
        "\n",
        "При использовании зависимого от данных порядка выполнения внутри` tf.function`, вы можете использовать операторы порядка выполнения Python, и AutoGraph преобразует их в соответствующие операции TensorFlow. Например, операторы `if` будут преобразованы в` tf.cond () ` если они зависят от `Tensor`-а.\n",
        "\n",
        "В примере ниже `x` - `Tensor` но команда `if` работает как ожидалось:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "aA3gOodCBkOw"
      },
      "outputs": [],
      "source": [
        "@tf.function\n",
        "def square_if_positive(x):\n",
        "  if x > 0:\n",
        "    x = x * x\n",
        "  else:\n",
        "    x = 0\n",
        "  return x\n",
        "\n",
        "\n",
        "print('square_if_positive(2) = {}'.format(square_if_positive(tf.constant(2))))\n",
        "print('square_if_positive(-2) = {}'.format(square_if_positive(tf.constant(-2))))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GMiCUkdyoq98"
      },
      "source": [
        "Примечание: предыдущий пример использует простые условные выражения со скалярными значениями. <a href=\"#batching\">Batching</a> обычно используется в коде для продакшна."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "m-jWmsCmByyw"
      },
      "source": [
        "AutoGraph поддерживает обычные команды Python такие как `while`, `for`, `if`, `break`, `continue` и `return`, с поддержкой вложенности. Это значит что вы можете использовать `Tensor` выражения в условиях команд `while` и `if`, или итерировать по `Tensor`-у в цикле `for`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "toxKBOXbB1ro"
      },
      "outputs": [],
      "source": [
        "@tf.function\n",
        "def sum_even(items):\n",
        "  s = 0\n",
        "  for c in items:\n",
        "    if c % 2 > 0:\n",
        "      continue\n",
        "    s += c\n",
        "  return s\n",
        "\n",
        "\n",
        "sum_even(tf.constant([10, 12, 15, 20]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AtDaLrbySw4j"
      },
      "source": [
        "AutoGraph также предоставляет низкоуровневый API для опытных пользователей. Например, мы можем использовать его, чтобы взглянуть на сгенерированный код."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "aRsde3x_SjTQ"
      },
      "outputs": [],
      "source": [
        "print(tf.autograph.to_code(sum_even.python_function))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rvJXCfk8VkLf"
      },
      "source": [
        "Вот пример более сложного порядка выполнения:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "h-Z87IJqVlKl"
      },
      "outputs": [],
      "source": [
        "@tf.function\n",
        "def fizzbuzz(n):\n",
        "  for i in tf.range(n):\n",
        "    if i % 3 == 0:\n",
        "      tf.print('Fizz')\n",
        "    elif i % 5 == 0:\n",
        "      tf.print('Buzz')\n",
        "    else:\n",
        "      tf.print(i)\n",
        "\n",
        "fizzbuzz(tf.constant(15))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "h_Y4uC1R1B55"
      },
      "source": [
        "## Keras и AutoGraph\n",
        "\n",
        "AutoGraph доступен по умолчанию в нединамических моделях Keras. Для получения дополнительной информации смотрите` tf.keras`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "cR6mpLKP1HLe"
      },
      "outputs": [],
      "source": [
        "class CustomModel(tf.keras.models.Model):\n",
        "\n",
        "  @tf.function\n",
        "  def call(self, input_data):\n",
        "    if tf.reduce_mean(input_data) > 0:\n",
        "      return input_data\n",
        "    else:\n",
        "      return input_data // 2\n",
        "\n",
        "\n",
        "model = CustomModel()\n",
        "\n",
        "model(tf.constant([-2, -4]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NTEvpBK9f8kj"
      },
      "source": [
        "## Побочные эффекты\n",
        "\n",
        "Как и в активном режиме, вы можете использовать операции с побочными эффектами, такие как` tf.assign` или `tf.print`, используемые обычно внутри` tf.function`, и он будет вставлять необходимые управляющие зависимости, чтобы обеспечить их выполнение в нужном порядке."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-Wd6i8S9gcuC"
      },
      "outputs": [],
      "source": [
        "v = tf.Variable(5)\n",
        "\n",
        "@tf.function\n",
        "def find_next_odd():\n",
        "  v.assign(v + 1)\n",
        "  if v % 2 == 0:\n",
        "    v.assign(v + 1)\n",
        "\n",
        "\n",
        "find_next_odd()\n",
        "v"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4LfnJjm0Bm0B"
      },
      "source": [
        "<a id=\"debugging\"></a>\n",
        "\n",
        "## Отладка\n",
        "\n",
        "` tf.function` и AutoGraph работают, генерируя код и копируя его в графы TensorFlow. Этот механизм пока не поддерживает пошаговые отладчики, такие как `pdb`. Однако вы можете вызвать `tf.config.run_functions_eagerly(True)` временно включить eager execution внутри `tf.function` и использовать ваш любимый отладчик:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Yci8ve6hmgpF"
      },
      "outputs": [],
      "source": [
        "@tf.function\n",
        "def f(x):\n",
        "  if x > 0:\n",
        "    # Попробуйте установить точку остановки тут!\n",
        "    # Example:\n",
        "    #   import pdb\n",
        "    #   pdb.set_trace()\n",
        "    x = x + 1\n",
        "  return x\n",
        "\n",
        "tf.config.experimental_run_functions_eagerly(True)\n",
        "\n",
        "# Сейчас вы можете установить точки остановки и запустить код в отладчике.\n",
        "f(tf.constant(1))\n",
        "\n",
        "tf.config.experimental_run_functions_eagerly(False)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NfpIQUv28Ht4"
      },
      "source": [
        "## Продвинутый пример: цикл обучения в графе\n",
        "\n",
        "Предыдущий раздел показал, что AutoGraph можно использовать внутри слоев и моделей Keras. Модели Keras также могут быть использованы в коде AutoGraph.\n",
        "\n",
        "Этот пример показывает, как обучить простую модель Keras в MNIST со всем процессом обучения - загрузкой пакетов, вычислением градиентов, обновлением параметров, вычислением точности валидации и повторением до сходимости - выполняемом в графе."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Em5dzSUOtLRP"
      },
      "source": [
        "### Скачайте данные"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "xqoxumv0ssQW"
      },
      "outputs": [],
      "source": [
        "def prepare_mnist_features_and_labels(x, y):\n",
        "  x = tf.cast(x, tf.float32) / 255.0\n",
        "  y = tf.cast(y, tf.int64)\n",
        "  return x, y\n",
        "\n",
        "def mnist_dataset():\n",
        "  (x, y), _ = tf.keras.datasets.mnist.load_data()\n",
        "  ds = tf.data.Dataset.from_tensor_slices((x, y))\n",
        "  ds = ds.map(prepare_mnist_features_and_labels)\n",
        "  ds = ds.take(20000).shuffle(20000).batch(100)\n",
        "  return ds\n",
        "\n",
        "train_dataset = mnist_dataset()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "znmy4l8ntMvW"
      },
      "source": [
        "### Определите модель"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ltxyJVWTqNAO"
      },
      "outputs": [],
      "source": [
        "model = tf.keras.Sequential((\n",
        "    tf.keras.layers.Reshape(target_shape=(28 * 28,), input_shape=(28, 28)),\n",
        "    tf.keras.layers.Dense(100, activation='relu'),\n",
        "    tf.keras.layers.Dense(100, activation='relu'),\n",
        "    tf.keras.layers.Dense(10)))\n",
        "model.build()\n",
        "optimizer = tf.keras.optimizers.Adam()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "oeYV6mKnJGMr"
      },
      "source": [
        "### Определите цикл обучения"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3xtg_MMhJETd"
      },
      "outputs": [],
      "source": [
        "compute_loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)\n",
        "\n",
        "compute_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()\n",
        "\n",
        "\n",
        "def train_one_step(model, optimizer, x, y):\n",
        "  with tf.GradientTape() as tape:\n",
        "    logits = model(x)\n",
        "    loss = compute_loss(y, logits)\n",
        "\n",
        "  grads = tape.gradient(loss, model.trainable_variables)\n",
        "  optimizer.apply_gradients(zip(grads, model.trainable_variables))\n",
        "\n",
        "  compute_accuracy(y, logits)\n",
        "  return loss\n",
        "\n",
        "\n",
        "@tf.function\n",
        "def train(model, optimizer):\n",
        "  train_ds = mnist_dataset()\n",
        "  step = 0\n",
        "  loss = 0.0\n",
        "  accuracy = 0.0\n",
        "  for x, y in train_ds:\n",
        "    step += 1\n",
        "    loss = train_one_step(model, optimizer, x, y)\n",
        "    if step % 10 == 0:\n",
        "      tf.print('Step', step, ': loss', loss, '; accuracy', compute_accuracy.result())\n",
        "  return step, loss, accuracy\n",
        "\n",
        "step, loss, accuracy = train(model, optimizer)\n",
        "print('Final step', step, ': loss', loss, '; accuracy', compute_accuracy.result())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SnsumiP6eRYL"
      },
      "source": [
        "## Батчинг (разбивка на пакеты)\n",
        "\n",
        "В реальных приложениях разбивка на пакеты важна для производительности. Лучший код для преобразования в AutoGraph - это код, в котором порядок выполнения определяется на уровне _batch_. Если вы принимаете решения на уровне отдельного _example_, попробуйте использовать API пакетной обработки для сохранения производительности.\n",
        "\n",
        "Например, если у вас следующий код в Python:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "t31QoERiNccJ"
      },
      "outputs": [],
      "source": [
        "def square_if_positive(x):\n",
        "  return [i ** 2 if i > 0 else i for i in x]\n",
        "\n",
        "\n",
        "square_if_positive(range(-5, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kSeEJ76uNgwD"
      },
      "source": [
        "Возможно, у вас возникнет соблазн написать его в TensorFlow следующим образом (и это будет работать!):\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "RqR8WzSzNf87"
      },
      "outputs": [],
      "source": [
        "@tf.function\n",
        "def square_if_positive_naive(x):\n",
        "  result = tf.TensorArray(tf.int32, size=x.shape[0])\n",
        "  for i in tf.range(x.shape[0]):\n",
        "    if x[i] > 0:\n",
        "      result = result.write(i, x[i] ** 2)\n",
        "    else:\n",
        "      result = result.write(i, x[i])\n",
        "  return result.stack()\n",
        "\n",
        "\n",
        "square_if_positive_naive(tf.range(-5, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "gTcyWXVGN3gS"
      },
      "source": [
        "Но в этом случае, оказывается вы можете написать следующее:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "VO2f6x-lNfVj"
      },
      "outputs": [],
      "source": [
        "def square_if_positive_vectorized(x):\n",
        "  return tf.where(x > 0, x ** 2, x)\n",
        "\n",
        "\n",
        "square_if_positive_vectorized(tf.range(-5, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EXFVx-cJ57_F"
      },
      "source": [
        "## Ре-трассировка\n",
        "\n",
        "Ключевые моменты:\n",
        "\n",
        "* Соблюдайте осторожность при вызове функций с нетензорными аргументами или с аргументами, которые изменяют размерности.\n",
        "* Декорируйте функции уровня модуля и методы классов уровня модуля и избегайте декорирования локальных функций или методов.\n",
        "\n",
        "`tf.function` может дать вам значительное ускорение относительно eager execution, цена этому более медленный первый запуск. Это потому что при первом запуске функция также *трассируется* в граф TensorFlow. Построение и оптимизация графа обычно намного медленнее его исполнения:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "iahT-4wT6vlA"
      },
      "outputs": [],
      "source": [
        "import timeit\n",
        "\n",
        "\n",
        "@tf.function\n",
        "def f(x, y):\n",
        "  return tf.matmul(x, y)\n",
        "\n",
        "print(\n",
        "    \"Первый вызов:\",\n",
        "    timeit.timeit(lambda: f(tf.ones((10, 10)), tf.ones((10, 10))), number=1))\n",
        "\n",
        "print(\n",
        "    \"Второй вызов:\",\n",
        "    timeit.timeit(lambda: f(tf.ones((10, 10)), tf.ones((10, 10))), number=1))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "q0Wojo2Z7hKg"
      },
      "source": [
        "Вы можете легко определить, когда произошла трассировка функции, добавив оператор `print` в начало функции. Поскольку любой код Python выполняется только во время трассировки, вы увидите результат `print`, при трассировке функции:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2IHE7-jT7gZs"
      },
      "outputs": [],
      "source": [
        "@tf.function\n",
        "def f():\n",
        "  print('Tracing!')\n",
        "  tf.print('Executing')\n",
        "\n",
        "print('Первый вызов:')\n",
        "f()\n",
        "\n",
        "print('Второй вызов:')\n",
        "f()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "J2VBoQC58PdU"
      },
      "source": [
        "`tf.function` может также *ре-трассировать* при вызове других нетензорных аргументов:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-c6VUwrz808l"
      },
      "outputs": [],
      "source": [
        "@tf.function\n",
        "def f(n):\n",
        "  print(n, 'Трассировка!')\n",
        "  tf.print(n, 'Выполненние')\n",
        "\n",
        "f(1)\n",
        "f(1)\n",
        "\n",
        "f(2)\n",
        "f(2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aKOrjBLCE8cy"
      },
      "source": [
        "*Ре-трассировка* может также произойти когда тензорные аргументы поменяли размерность, если вы не указали `input_signature`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "txhtkn0rE8dH"
      },
      "outputs": [],
      "source": [
        "@tf.function\n",
        "def f(x):\n",
        "  print(x.shape, 'Трассировка!')\n",
        "  tf.print(x, 'Выполненние')\n",
        "\n",
        "f(tf.constant([1]))\n",
        "f(tf.constant([2]))\n",
        "\n",
        "f(tf.constant([1, 2]))\n",
        "f(tf.constant([3, 4]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sdN40ZqT9XaG"
      },
      "source": [
        "В дополнение, tf.function всегда создает новую функцию графа со своим наборов трассировок при каждом вызове:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "GT1iBa5i9enE"
      },
      "outputs": [],
      "source": [
        "def f():\n",
        "  print('Трассировка!')\n",
        "  tf.print('Выполнение')\n",
        "\n",
        "tf.function(f)()\n",
        "tf.function(f)()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "n7_JDzFK9nnC"
      },
      "source": [
        "Это может привести к неожиданному поведению при использовании декоратора `@tf.function` во вложенной функции:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "P3pBG7Uf9u4g"
      },
      "outputs": [],
      "source": [
        "def outer():\n",
        "  @tf.function\n",
        "  def f():\n",
        "    print('Трассировка!')\n",
        "    tf.print('Выполнение')\n",
        "  f()\n",
        "\n",
        "outer()\n",
        "outer()"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [
        "Jxv6goXm7oGF"
      ],
      "name": "function.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
